package api

import (
	"fmt"
	"goadmin/conf"
	"goadmin/dao"
	"goadmin/model"
	"goadmin/utils"

	"github.com/gin-gonic/gin"
)

// list
type _userRes struct {
	Id       uint   `json:"id"`
	Username string `json:"username"`
	Nickname string `json:"nickname"`
	Avatar   string `json:"avatar"`
	Auth     int    `json:"auth"`
}

// register
type _userRegisterReq struct {
	Username string `json:"username" binding:"required,username" example:"sd1"`
	Password string `json:"password" binding:"required,alphanum,min=1,max=20" example:"123456"`
	Nickname string `json:"nickname" binding:"omitempty,max=20" example:"nickname"`
}

// change avatar
type _userChangeAvatarReq struct {
	Avatar string `json:"avatar" binding:"required"`
}

// change password
type _userChangePwdReq struct {
	OldPassword string `json:"oldPassword" binding:"required,alphanum,min=1,max=20" example:"123456"`
	NewPassword string `json:"newPassword" binding:"required,alphanum,min=1,max=20" example:"123456"`
}

// change info
type _userChangeInfoReq struct {
	Nickname string `json:"nickname" binding:"required,max=20" example:"nickname"`
}

// batch delete
type _userBatchDelReq struct {
	Ids []uint `json:"ids" binding:"required"`
}

// @Tags user
// @Summary User info
// @Accept json
// @Produce json
// @Security ApiKeyAuth
// @Param id path uint true "User ID"
// @Success 200 {object} resBody{data=_userRes}
// @Router /api/users/{id} [get]
func UserInfo(c *gin.Context) {
	userId := c.GetUint("user_id")
	curUser, err := dao.GetUserByIdT[_userRes](userId)
	if err != nil {
		resNoAuth(c)
		return
	}
	pathId := c.GetUint("path_id")

	resUser := curUser
	if pathId != userId {
		tarUser, err := dao.GetUserByIdT[_userRes](pathId)
		if err != nil {
			resFai(c, "User does not exist")
			return
		}
		if tarUser.Auth >= curUser.Auth {
			resPermissionDenied(c)
			return
		}
		resUser = tarUser
	}
	resSuc(c, resUser)
}

// @Tags user
// @Summary User self info
// @Accept json
// @Produce json
// @Security ApiKeyAuth
// @Success 200 {object} resBody{data=_userRes}
// @Router /api/users/me [get]
func UserSelfInfo(c *gin.Context) {
	userId := c.GetUint("user_id")
	c.Redirect(302, fmt.Sprintf("/api/users/%d", userId))
}

// @Tags user
// @Summary User list
// @Accept json
// @Produce json
// @Security ApiKeyAuth
// @Param username query string false "Username"
// @Param nickname query string false "Nickname"
// @Param page query int false "Page"
// @Param page-size query int false "Page Size"
// @Success 200 {object} resBody{data=list{items=[]_userRes}}
// @Router /api/users [get]
func UserList(c *gin.Context) {
	userId := c.GetUint("user_id")
	// curUser, err := dao.GetUserById(userId)
	_, err := dao.GetUserById(userId)
	if err != nil {
		resNoAuth(c)
		return
	}

	var query struct {
		paginationQuery
		Username string `form:"username" binding:"omitempty"`
		Nickname string `form:"nickname" binding:"omitempty"`
	}
	if err := c.ShouldBindQuery(&query); err != nil {
		resInvalidQuery(c)
		return
	}

	var conditions []dao.Condition
	// conditions = append(conditions, dao.UserAuthLessThan(curUser.Auth))
	if query.Username != "" {
		conditions = append(conditions, dao.UserLikeUsername(query.Username))
	}
	if query.Nickname != "" {
		conditions = append(conditions, dao.UserLikeNickname(query.Nickname))
	}

	users, total, err := dao.GetUserList[_userRes](query.Page, query.PageSize, conditions...)
	if err != nil {
		resFai(c, "Failed to get user list")
		return
	}
	resSuc(c, &list{Total: total, Items: users})
}

// @Tags user
// @Summary Register
// @Accept json
// @Produce json
// @Param body body _userRegisterReq true "Request Body"
// @Success 200 {object} resBody
// @Router /api/users [post]
func UserRegister(c *gin.Context) {
	if !conf.App.RegisterEnable {
		resFai(c, "Registration is not allowed")
		return
	}

	var body _userRegisterReq
	if err := c.ShouldBindJSON(&body); err != nil {
		resFai(c, err.Error())
		return
	}

	if _, err := dao.GetUserByUsername(body.Username); err == nil {
		resFai(c, "Username already exists")
		return
	}

	user := model.User{
		Username: body.Username,
		Password: body.Password,
		Nickname: body.Nickname,
	}

	if err := dao.InsertUser(user); err != nil {
		resFai(c, "Failed to register")
		return
	}
	resSuc(c, nil)
}

// @Tags user
// @Summary Change avatar
// @Accept json
// @Produce json
// @Security ApiKeyAuth
// @Param body body _userChangeAvatarReq true "Request Body"
// @Success 200 {object} resBody
// @Router /api/users/me/avatar [put]
func UserChangeAvatar(c *gin.Context) {
	userId := c.GetUint("user_id")
	user, err := dao.GetUserById(userId)
	if err != nil {
		resNoAuth(c)
		return
	}

	var body _userChangeAvatarReq
	if err := c.ShouldBindJSON(&body); err != nil {
		resInvalidBody(c)
		return
	}

	user.Avatar = body.Avatar
	if err := dao.UpdateUserByUser(user); err != nil {
		resFai(c, "Failed to change avatar")
		return
	}

	resSuc(c, nil)
}

// @Tags user
// @Summary Change password
// @Accept json
// @Produce json
// @Security ApiKeyAuth
// @Param body body _userChangePwdReq true "Request Body"
// @Success 200 {object} resBody
// @Router /api/users/me/password [put]
func UserChangePassword(c *gin.Context) {
	userId := c.GetUint("user_id")
	user, err := dao.GetUserById(userId)
	if err != nil {
		resNoAuth(c)
		return
	}

	var body _userChangePwdReq
	if err := c.ShouldBindJSON(&body); err != nil {
		resInvalidBody(c)
		return
	}

	if err := utils.VerifyPwdHash(body.OldPassword, user.Password); err != nil {
		resFai(c, "Incorrect old password")
		return
	}

	hash, err := utils.GeneratePwdHash(body.NewPassword)
	if err != nil {
		resFai(c, "Failed to change password")
		return
	}

	user.Password = hash
	if err := dao.UpdateUserByUser(user); err != nil {
		resFai(c, "Failed to change password")
		return
	}

	resSuc(c, nil)
}

// @Tags user
// @Summary Change info
// @Accept json
// @Produce json
// @Security ApiKeyAuth
// @Param body body _userChangeInfoReq true "Request Body"
// @Success 200 {object} resBody
// @Router /api/users/me/info [put]
func UserChangeInfo(c *gin.Context) {
	userId := c.GetUint("user_id")
	user, err := dao.GetUserById(userId)
	if err != nil {
		resNoAuth(c)
		return
	}
	var body _userChangeInfoReq
	if err := c.ShouldBindJSON(&body); err != nil {
		resInvalidBody(c)
		return
	}
	user.Nickname = body.Nickname
	if err := dao.UpdateUserByUser(user); err != nil {
		resFai(c, "Failed to change nickname")
		return
	}
	resSuc(c, nil)
}

// @Tags user
// @Summary User delete
// @Accept json
// @Produce json
// @Security ApiKeyAuth
// @Param id path uint true "User ID"
// @Success 200 {object} resBody
// @Router /api/users/{id} [delete]
func UserDel(c *gin.Context) {
	userId := c.GetUint("user_id")
	curUser, err := dao.GetUserById(userId)
	if err != nil {
		resNoAuth(c)
		return
	}

	pathId := c.GetUint("path_id")

	tarUser, err := dao.GetUserById(pathId)
	if err != nil {
		resFai(c, "User does not exist")
		return
	}

	if tarUser.Auth >= curUser.Auth {
		resPermissionDenied(c)
		return
	}

	if err := dao.DelUserById(pathId); err != nil {
		resFai(c, "Failed to delete user")
		return
	}

	resSuc(c, nil)
}

// @Tags user
// @Summary User batch delete
// @Accept json
// @Produce json
// @Security ApiKeyAuth
// @Param body body _userBatchDelReq true "Request Body"
// @Success 200 {object} resBody
// @Router /api/users/batch_delete [post]
func UserBatchDel(c *gin.Context) {
	userId := c.GetUint("user_id")
	curUser, err := dao.GetUserById(userId)
	if err != nil {
		resNoAuth(c)
		return
	}

	var body _userBatchDelReq
	if err := c.ShouldBindJSON(&body); err != nil {
		resInvalidBody(c)
		return
	}

	if err := dao.DelUserByIds(curUser.Auth, body.Ids); err != nil {
		resFai(c, "Failed to delete user")
		return
	}

	resSuc(c, nil)
}
